[SimplifyLibCalls] Directly convert fmin/fmax to intrinsics - #177988
Conversation
|
@llvm/pr-subscribers-llvm-transforms Author: Nikita Popov (nikic) ChangesDrop the custom shrinking code, which we'll also do for intrinsics. Having libcall-only optimizations is confusing, as these are typically directly emitted as intrinsics by the frontend. However, the fold on intrinsics is limited to one-use fpexts, so the behavior is not completely the same: llvm-project/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp Lines 2870 to 2877 in d7666c6 I think we could relax that condition to only one of the fpexts being one-use. Let me know if you want me to do that. Full diff: https://github.com/llvm/llvm-project/pull/177988.diff 3 Files Affected:
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index 64d2512308935..9ba3455254f47 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -204,7 +204,7 @@ class LibCallSimplifier {
Value *replacePowWithExp(CallInst *Pow, IRBuilderBase &B);
Value *replacePowWithSqrt(CallInst *Pow, IRBuilderBase &B);
Value *optimizeExp2(CallInst *CI, IRBuilderBase &B);
- Value *optimizeFMinFMax(CallInst *CI, IRBuilderBase &B);
+ Value *optimizeFMinFMax(CallInst *CI, IRBuilderBase &B, Intrinsic::ID IID);
Value *optimizeFMinimumnumFMaximumnum(CallInst *CI, IRBuilderBase &B);
Value *optimizeLog(CallInst *CI, IRBuilderBase &B);
Value *optimizeSqrt(CallInst *CI, IRBuilderBase &B);
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 1e8a184fc51b8..ff05faf1c2200 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -2520,17 +2520,8 @@ Value *LibCallSimplifier::optimizeExp2(CallInst *CI, IRBuilderBase &B) {
return Ret;
}
-Value *LibCallSimplifier::optimizeFMinFMax(CallInst *CI, IRBuilderBase &B) {
- Module *M = CI->getModule();
-
- // If we can shrink the call to a float function rather than a double
- // function, do that first.
- Function *Callee = CI->getCalledFunction();
- StringRef Name = Callee->getName();
- if ((Name == "fmin" || Name == "fmax") && hasFloatVersion(M, Name))
- if (Value *Ret = optimizeBinaryDoubleFP(CI, B, TLI))
- return Ret;
-
+Value *LibCallSimplifier::optimizeFMinFMax(CallInst *CI, IRBuilderBase &B,
+ Intrinsic::ID IID) {
// The LLVM intrinsics minnum/maxnum correspond to fmin/fmax. Canonicalize to
// the intrinsics for improved optimization (for example, vectorization).
// No-signed-zeros is implied by the definitions of fmax/fmin themselves.
@@ -2540,9 +2531,6 @@ Value *LibCallSimplifier::optimizeFMinFMax(CallInst *CI, IRBuilderBase &B) {
// might be impractical."
FastMathFlags FMF = CI->getFastMathFlags();
FMF.setNoSignedZeros();
-
- Intrinsic::ID IID = Callee->getName().starts_with("fmin") ? Intrinsic::minnum
- : Intrinsic::maxnum;
return copyFlags(*CI, B.CreateBinaryIntrinsic(IID, CI->getArgOperand(0),
CI->getArgOperand(1), FMF));
}
@@ -4147,10 +4135,11 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
case LibFunc_fminf:
case LibFunc_fmin:
case LibFunc_fminl:
+ return optimizeFMinFMax(CI, Builder, Intrinsic::minnum);
case LibFunc_fmaxf:
case LibFunc_fmax:
case LibFunc_fmaxl:
- return optimizeFMinFMax(CI, Builder);
+ return optimizeFMinFMax(CI, Builder, Intrinsic::maxnum);
case LibFunc_fminimum_numf:
case LibFunc_fminimum_num:
case LibFunc_fminimum_numl:
diff --git a/llvm/test/Transforms/InstCombine/float-shrink-compare.ll b/llvm/test/Transforms/InstCombine/float-shrink-compare.ll
index 6383feff3a6ee..452df5118a324 100644
--- a/llvm/test/Transforms/InstCombine/float-shrink-compare.ll
+++ b/llvm/test/Transforms/InstCombine/float-shrink-compare.ll
@@ -507,8 +507,10 @@ define i1 @test19(float %x, float %y, float %z) {
define i1 @test20(float %x, float %y) {
; CHECK-LABEL: @test20(
-; CHECK-NEXT: [[FMINF:%.*]] = call nsz float @llvm.minnum.f32(float [[X:%.*]], float 1.000000e+00)
-; CHECK-NEXT: [[TMP1:%.*]] = fcmp oeq float [[Y:%.*]], [[FMINF]]
+; CHECK-NEXT: [[TMP4:%.*]] = fpext float [[Y:%.*]] to double
+; CHECK-NEXT: [[TMP2:%.*]] = fpext float [[X:%.*]] to double
+; CHECK-NEXT: [[TMP3:%.*]] = call nsz double @llvm.minnum.f64(double [[TMP2]], double 1.000000e+00)
+; CHECK-NEXT: [[TMP1:%.*]] = fcmp oeq double [[TMP3]], [[TMP4]]
; CHECK-NEXT: ret i1 [[TMP1]]
;
%1 = fpext float %y to double
|
🪟 Windows x64 Test Results
Failed Tests(click on a test name to see its output) LLVMLLVM.CodeGen/AMDGPU/kernel-args.llIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
🐧 Linux x64 Test Results
Failed Tests(click on a test name to see its output) LLVMLLVM.CodeGen/AMDGPU/kernel-args.llIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
dtcxzyw
left a comment
There was a problem hiding this comment.
I think we could relax that condition to only one of the fpexts being one-use. Let me know if you want me to do that.
Make sense to me. But relaxing the one-use constraint doesn't restore the test change. Note that the LHS is a constant, and valueHasFloatPrecision is responsible for converting it to float losslessly.
llvm-project/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
Lines 1956 to 1963 in 6233946
Yeah, not sure what I was looking at when I wrote this... I've now opened both #179968 (for the constant case) and #180164 (for the multi-use case), both of which are profitable on llvm-opt-benchmark. |
#179968) Fold `min/max(fpext x, C)` to `fpext(min/max(x, fptrunc C))` in cases where the truncation of the constant is lossless. This helps eliminate fpext/fptrunc pairs around min/max and addresses the regression from #177988. Proof: https://alive2.llvm.org/ce/z/y_Bcdd
Drop the custom shrinking code, which we'll also do for intrinsics. Having libcall-only optimizations is confusing, as these are typically directly emitted as intrinsics by the frontend.
… fptrunc C)) (#179968) Fold `min/max(fpext x, C)` to `fpext(min/max(x, fptrunc C))` in cases where the truncation of the constant is lossless. This helps eliminate fpext/fptrunc pairs around min/max and addresses the regression from llvm/llvm-project#177988. Proof: https://alive2.llvm.org/ce/z/y_Bcdd
llvm#179968) Fold `min/max(fpext x, C)` to `fpext(min/max(x, fptrunc C))` in cases where the truncation of the constant is lossless. This helps eliminate fpext/fptrunc pairs around min/max and addresses the regression from llvm#177988. Proof: https://alive2.llvm.org/ce/z/y_Bcdd
…ntrinsic (#180555) Same as llvm/llvm-project#177988, but for fminimum_num/fmaximum_num. Directly canonicalize these to the corresponding intrinsics, and let the shrinking happen directly on the intrinsics.
…) (#179968) Fold `min/max(fpext x, C)` to `fpext(min/max(x, fptrunc C))` in cases where the truncation of the constant is lossless. This helps eliminate fpext/fptrunc pairs around min/max and addresses the regression from llvm/llvm-project#177988. Proof: https://alive2.llvm.org/ce/z/y_Bcdd
…180555) Same as llvm/llvm-project#177988, but for fminimum_num/fmaximum_num. Directly canonicalize these to the corresponding intrinsics, and let the shrinking happen directly on the intrinsics.
Drop the custom shrinking code, which we'll also do for intrinsics. Having libcall-only optimizations is confusing, as these are typically directly emitted as intrinsics by the frontend.